﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Web;
using System.Threading;
using elmah2.core.encoders;

namespace elmah2.core.data
{
  [Serializable]
  public sealed class Error : ICloneable
  {
    public string ID { get; set; }
    public Exception Exception { get; private set; }
    public string ApplicationName { get; set; }
    public string HostName { get; set; }
    public string Type { get; set; }
    public string Source { get; set; }
    public string Message { get; set; }
    public string Detail { get; set; }
    public string User { get; set; }
    public DateTime Time { get; set; }
    public int StatusCode { get; set; }
    public string WebHostHtmlMessage { get; set; }
    public string EncodedString { get; set; }

    public NameValueCollection ServerVariables { get; private set; }
    public NameValueCollection QueryString { get; private set; }
    public NameValueCollection Form { get; private set; }
    public NameValueCollection Cookies { get; private set; }


    public Error() 
    {
      ServerVariables = new NameValueCollection();
      QueryString = new NameValueCollection();
      Form = new NameValueCollection();
      Cookies = new NameValueCollection();
    }

    public Error(Exception e) :
      this(e, null) { }

    public Error(Exception e, HttpContext context)
    {
      if (e == null)
        throw new ArgumentNullException("e");

      Exception = e;
      Exception baseException = e.GetBaseException();

      HostName = Environment.MachineName;
      Type = baseException.GetType().FullName;
      Message = baseException.Message;
      Source = baseException.Source;
      Detail = e.ToString();
      User = Thread.CurrentPrincipal.Identity.Name;
      Time = DateTime.Now;

      HttpException httpException = e as HttpException;

      if (httpException != null)
      {
        StatusCode = httpException.GetHttpCode();
        WebHostHtmlMessage = httpException.GetHtmlErrorMessage();
      }

      if (context != null)
      {
        HttpRequest request = context.Request;

        ServerVariables = CopyCollection(request.ServerVariables);
        QueryString = CopyCollection(request.QueryString);
        Form = CopyCollection(request.Form);
        Cookies = CopyCollection(request.Cookies);
      }
      else
      {
        ServerVariables = new NameValueCollection();
        QueryString = new NameValueCollection();
        Form = new NameValueCollection();
        Cookies = new NameValueCollection();
      }
    }

    public string Encode()
    {
      return XmlEncoder.EncodeString(this);
    }

    public override string ToString()
    {
      return Message;
    }

    object ICloneable.Clone()
    {
      Error copy = (Error)MemberwiseClone();

      copy.ServerVariables = CopyCollection(ServerVariables);
      copy.QueryString = CopyCollection(QueryString);
      copy.Form = CopyCollection(Form);
      copy.Cookies = CopyCollection(Cookies);

      return copy;
    }

    private static NameValueCollection CopyCollection(NameValueCollection collection)
    {
      if (collection == null || collection.Count == 0)
        return null;

      return new NameValueCollection(collection);
    }

    private static NameValueCollection CopyCollection(HttpCookieCollection cookies)
    {
      if (cookies == null || cookies.Count == 0)
        return null;

      NameValueCollection copy = new NameValueCollection(cookies.Count);

      for (int i = 0; i < cookies.Count; i++)
      {
        HttpCookie cookie = cookies[i];
        copy.Add(cookie.Name, cookie.Value);
      }

      return copy;
    }
  }
}
